
import java.net.*;
import java.util.Random;
import java.io.*;


public class TcpClient {

	private static Random randomGenerator = new Random();
	private static int numServers = Constants.NUM_SERVERS;
	private BufferedReader stdIn;

	public static void main(String[] args) {
		new TcpClient();
	}

	public TcpClient() {
		stdIn = new BufferedReader(new InputStreamReader(System.in));
		run();	
	}

	private void run() {

		try {
			System.out.println("TCP Client Started");
			while (true) {
				try {
					printMenu();
					String requestString = processUserInput();
					if (requestString.equals(Constants.EXIT)) {
						break;
					} else if(requestString.length()>0){
						System.out.println("------------------------------------");
						Socket socket = null;
						int serverId = randomGenerator.nextInt(numServers);
						System.out.print("Connecting");
						while (socket == null) {
							try {
								System.out.print(".");
								if (serverId >= numServers)
									serverId = 0;
								socket = new Socket(Constants.HOSTNAME, Constants.getPort(serverId));
							} catch (java.net.ConnectException e) {
								serverId = randomGenerator.nextInt(numServers);
							}
						}

						System.out.println("\nConnected to server #: " + serverId + "\n");

						BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
						PrintStream printStream = new PrintStream(socket.getOutputStream());
						printStream.println(requestString + "\n");
						printStream.flush();
						String responseString = bufferedReader.readLine();
						responseString = responseString.replace(Constants.CARRIAGE_RETURN, "\n");
						processeServerResponse(requestString, responseString);
						socket.close();
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void printMenu() {
		System.out.println("------------------------------------");
		System.out.println("\nPlease choose from the following: ");
		System.out.println("[1] make reservations");
		System.out.println("[2] search for a reservation");
		System.out.println("[3] delete a reservation ");
		System.out.println("[4] get movie information");
		System.out.println("[9] exit the system");
		System.out.println("[0] data integrity check accross all servers");
	}

	private String processUserInput() {
		String command = "";
		String name = "";
		String count = "";
		try {
			String option = stdIn.readLine().trim();
			if (option.equals("1")) {
				command = Constants.RESERVE;
				System.out.print("name? ");
				name = stdIn.readLine();
				System.out.print("# of tickets? ");
				count = stdIn.readLine();
			} else if (option.equals("2")) {
				command = Constants.SEARCH;
				System.out.print("name? ");
				name = stdIn.readLine();
			} else if (option.equals("3")) {
				command = Constants.DELETE;
				System.out.print("name? ");
				name = stdIn.readLine();
			} else if (option.equals("4")) {
				command = Constants.GET_INFO;
			} else if (option.equals("9")) {
				command = Constants.EXIT;
			} else if (option.equals("0")) {
				dataCheck();
			} else {
				throw new Exception();
			}
		} catch (Exception e) {
			System.out.println("Invalid Entry!!");
			printMenu();
			command = processUserInput();
		}
		return (command.trim() + " " + name.trim() + " " + count.trim()).trim();

	}

	private void dataCheck() {
		boolean status[] = new boolean[Constants.NUM_SERVERS];
		String database[] = new String[Constants.NUM_SERVERS];
		for (int i = 0; i < Constants.NUM_SERVERS; i++) {
			try {
				Socket socket = new Socket(Constants.HOSTNAME, Constants.getPort(i));
				BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				PrintStream printStream = new PrintStream(socket.getOutputStream());
				printStream.println(Constants.GET_DATA_DUMP + "\n");
				printStream.flush();
				String responseString = bufferedReader.readLine();
				database[i] = responseString.replace(Constants.CARRIAGE_RETURN, "\n");
				socket.close();
				status[i] = true;
			} catch (java.net.ConnectException e) {
				status[i] = false;
				database[i]= "";
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		boolean passed = true;
		System.out.println("------------------------------------");
		System.out.println("server status and database hash");
		String oneDatabase = null;
		for (int i = 0; i < Constants.NUM_SERVERS; i++) {
			if (status[i] && oneDatabase!=null) {
				passed = oneDatabase.equals(database[i]);
			}
			String summary = "server# " + i + ": ";
			if (status[i]) {
				summary += "  up \t";
				summary += database[i].hashCode();
				if (oneDatabase == null)
					oneDatabase = database[i];
			} else {
				summary += "down \t";
			}
			System.out.println(summary);
		}
		System.out.println("------------------------------------");
		if (passed) {
			System.out.println("PASS: All available servers have the same database.");
		} else {
			System.out.println("FAIL: Database integrity failed, the databases are not synchronized!!.");
		}
		System.out.println("------------------------------------");
		System.out.println("Data dump from the first available server:");
		System.out.println(oneDatabase);
		System.out.println("------------------------------------");

	}

	private void processeServerResponse(String request, String response) {
		request = request.trim();
		response = response.trim();
		if (request.startsWith(Constants.SEARCH)) {
			if (response.equals("-1")) {
				System.out.println("There is no reservation under this name.");
			} else {
				System.out.println("Your reservation is valid, your assigned seats are:\n" + response + ".");
			}
		} else if (request.startsWith(Constants.DELETE)) {
			if (response.equals("-1")) {
				System.out.println("There is no reservation under this name.");
			} else {
				System.out.println("Your reservation for " + response + " seats has been deleted.");
			}
		} else if (request.startsWith(Constants.GET_INFO)) {
			System.out.println(response);
		} else if (request.startsWith(Constants.RESERVE)) {
			if (response.equals("-1")) {
				System.out.println("The theater does not have enought seats.");
			} else if (response.equals("-2")) {
				System.out.println("A reservation under this name has already been made.");
			} else {
				System.out.println("Your reservation has been processed, your assigned seats are:\n" + response + ".");
			}
		}
	}

}